#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashtable.h"


/*
 * Funzione per ricavare la posizione delle info dell'utente sulla base
 * dell'username
 *
 * @param Vettore di caratteri contenente l'username
 * @return Intero che rappresenta la posizione dell'username nella Hashtable
 */

int hashFunc(char key[]) {

	//Dichiaro variabili utili al calcolo del valore di hash
	int i = 0, hashval = 0;

	//Calcolo il valore di hash basato sull'username
	for (i = 0; i < strlen(key); i++) {
		hashval = ((hashval * 256) + key[i]) % HL;
	}

	//Ritorno il valore di hash
	return hashval;
}


/*
 * Procedura per inserire un utente nella Hashtable
 *
 * @param Riferimento alla hashtable
 * @param Vettore di caratteri contenente l'username dell'utente
 * @param Vettore di caratteri contenente il nome e cognome dell'utente
 * @param Vettore di caratteri contenente l'email dell'utente
 * @param Intero contenente il descrittore del socket
 */
void inserisciHashtable(hashcell *Table[], char username[], char name[], char email[], int descr) {

	//Ottengo la posizione della testa della lista a cui corrisponde l'username
	int pos = hashFunc(username);

	//Creo un nuovo elemento e assegno ai suoi campi i valori dei parametri
	hashcell * temp = malloc(sizeof(hashcell));
	if (temp == NULL)
		fprintf(stderr, "Errore durante l'inserimento dell'utente nella Hashtable");
	else {
		strcpy(temp -> username, username);
		strcpy(temp -> name, name);
		strcpy(temp -> email, email);
		temp -> descr = descr;
	}

	//Aggiungo l'elemento temporaneo in testa alla lista
	temp -> next = Table[pos];
	Table[pos] = temp;
}


/*
 * Funzione per cercare l'username nella Hashtable
 *
 * @param Riferimento alla hashtable
 * @param Vettore di caratteri contenente l'username dell'utente da cercare
 * @return Puntatore all'elemento della Hashtable corrispondente all'username
 * 		oppure NULL
 */
hashcell *ricercaUsername(hashcell *Table[], char username[]) {

	//Ottengo la posizione della testa della lista a cui corrisponde l'username
	int pos = hashFunc(username);

	//Scorro la lista finchè non ottengo una corrispondenza con l'username
	hashcell * temp = Table[pos];
	while (temp != NULL) {

		//Controllo che l'username sia nell'elemento che sto controllando
		if (strcmp(username, temp -> username) == 0)

			//Return del descrittore, oppure null se il client non è connesso
			return temp;
		else

			//Scorro la lista verso l'elmento successivo
			temp = temp -> next;
	}

	//Non trovando l'username ritorno NULL
	return NULL;
}


/*
 * Funzione per cercare l'elemento nella hashtable, sulla base del descrittore
 * del socket
 *
 * @param Riferimento alla hashtable
 * @param Intero che rappresenta il descrittore del socket
 * @return Puntatore all'elemento della Hashtable corrispondente all'username
 * 		oppure NULL
 */
hashcell *ricercaDescrittore(hashcell *Table[], int descr) {

	//Dichiaro alcune variabili utili alla ricerca
	int i = 0;
	hashcell * temp;

	//Leggo ogni lista della hashtable
	while(i < HL){

		//Controllo se nella posizione i sia presente una lista di utenti
		temp = Table[i];
		while (temp != NULL) {

			//Controllo che il descrittore sia nell'elemento che sto controllando
			if (descr == temp -> descr)

				//Return dell'elemento
				return temp;
			else

				//Scorro la lista verso l'elemento successivo
				temp = temp -> next;
		}

		//Aggiorno l'indice della Hashtable
		i++;
	}

	//Non trovando l'username, ritorno NULL
	return NULL;
}


/*
 * Funzione per ottenere l'username di tutti gli utenti online basandosi sul
 * controllo dei descrittori (se descrittore diverso da -1, allora utente online)
 *
 * @param Riferimento alla hashtable
 * @param Vettore di caratteri che conterrà la lista degli username online
 * @param Intero, variabile globale, che indica il numero di thread attivi
 * @return Vettore di caratteri contenente la lista di tutti gli utenti online
 * 		oppure NULL
 */
void listaUtentiOnline(hashcell *Table[], char list[], int numThreadAttivi) {

	//Dichiaro alcune variabili utili alla ricerca
	int i = 0, num = 0;
	hashcell * temp;

	//Azzero il contenuto di list
	strcpy(list, "");

	//Leggo ogni lista della hashtable
	while((i < HL) && (num < numThreadAttivi)){

		//Controllo se nella posizione i sia presente una lista di utenti
		temp = Table[i];
		while (temp != NULL) {

			//Controllo che l'utente appena letto sia online
			if (temp -> descr != -1) {

				//Aumento la grandezza di list per contenere il nuovo username + ":"
				if(sizeof(list) < sizeof(int) * numThreadAttivi)
					list = realloc(list, strlen(temp -> username) + 1);
				num++;

				//Controllo se list è stato reallocato senza problemi
				if (list == NULL)
					fprintf(stderr, "Errore durante la lettura della Hashtable");

				//Se la reallocazione è andata a buon fine
				else {

					//Aggiungo a list l'utente appena controllato
					strcat(list, temp -> username);
					if (num < numThreadAttivi)
						strcat(list, ":");
				}
			}

			//Assegno a temp l'indirizzo della cella successiva
			temp = temp -> next;
		}

		//Aggiorno l'indice della Hashtable
		i++;
	}
}


/*
 * Funzione per ottenere il descrittore di tutti gli utenti online (se
 * descrittore diverso da -1, allora utente online)
 *
 * @param Riferimento alla hashtable
 * @param Vettore di interi che conterrà la lista dei descrittori online
 * @param Intero, variabile globale, che indica il numero di thread attivi
 */
void listaDescrittoriOnline(hashcell *Table[], int list[], int numThreadAttivi) {

	//Dichiaro alcune variabili utili alla ricerca
	int i = 0, num = 0;
	hashcell * temp;

	//Leggo ogni lista della hashtable
	while((i < HL) && (num < numThreadAttivi)){

		//Controllo se nella posizione i sia presente una lista di utenti
		temp = Table[i];
		while (temp != NULL) {

			//Controllo che l'utente appena letto sia online
			if (temp -> descr != -1) {

				//Copio in list il descrittore appena trovato
				list[num] = temp -> descr;
				num++;
			}

			//Assegno a temp l'indirizzo della cella successiva
			temp = temp -> next;
		}

		//Aggiorno l'indice della hashtable
		i++;
	}
}
